// Package db 包 db 提供数据库连接的单例与初始化逻辑
package db

import (
	"fmt"
	"log"
	"sync"
	"time"

	confpkg "gin_http/utils/config"
	utilsLooger "gin_http/utils/logger"

	"go.uber.org/zap"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

var (
	// 使用 sync.Once 保证数据库只初始化一次
	once sync.Once
	// 全局 *gorm.DB 连接句柄
	DB *gorm.DB
)

// GetDB 返回单例的 *gorm.DB
func GetDB() *gorm.DB {
	// 首次访问时进行初始化
	once.Do(initDB)
	// 返回全局数据库连接
	return DB
}

func initDB() {
	// 读取数据库配置
	cfg := confpkg.GetConfig().Database

	// 构建 Postgres 的 DSN；如需使用其他驱动请相应调整
	ssl := cfg.SSLMode
	if ssl == "" {
		// 开发环境默认关闭 SSL
		ssl = "disable"
	}
	tz := cfg.TimeZone
	if tz == "" {
		// 默认时区为 Asia/Shanghai
		tz = "Asia/Shanghai"
	}

	// 组装符合 mysql 的 DSN
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		cfg.User,
		cfg.Password,
		cfg.Host,
		cfg.Port,
		cfg.Name,
	)
	fmt.Println(dsn)

	// 为 GORM 使用 zap 日志（也可实现完整的 GORM 日志包装器）
	gormLogger := logger.New(
		// 使用标准库 log，将输出转发到 zap
		log.New(loggerWriter{zapLogger: utilsLooger.GetModuleLogger("db")}, "", log.LstdFlags),
		logger.Config{
			SlowThreshold:             time.Second,
			LogLevel:                  logger.Info,
			IgnoreRecordNotFoundError: true,
			Colorful:                  false,
		},
	)

	var err error
	// 打开数据库连接，并注入自定义日志器
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: gormLogger,
	})
	if err != nil {
		// 连接失败：记录错误并中止启动
		utilsLooger.GetModuleLogger("db").Error("数据库连接失败", zap.String("error", err.Error()))
		panic(err)
	}

	// 设置连接池参数
	sqlDB, err := DB.DB()
	if err != nil {
		utilsLooger.GetModuleLogger("db").Error("获取 sql.DB 失败", zap.String("error", err.Error()))
		panic(err)
	}

	if cfg.MaxIdleConns > 0 {
		// 最大空闲连接数
		sqlDB.SetMaxIdleConns(cfg.MaxIdleConns)
	} else {
		sqlDB.SetMaxIdleConns(10)
	}

	if cfg.MaxOpenConns > 0 {
		// 最大打开连接数
		sqlDB.SetMaxOpenConns(cfg.MaxOpenConns)
	} else {
		sqlDB.SetMaxOpenConns(100)
	}

	if cfg.ConnMaxLifetime > 0 {
		// 连接最大生命周期
		sqlDB.SetConnMaxLifetime(time.Duration(cfg.ConnMaxLifetime) * time.Second)
	} else {
		sqlDB.SetConnMaxLifetime(time.Hour)
	}

	// 初始化完成，打印成功日志
	utilsLooger.GetModuleLogger("db").Info("数据库初始化成功")
}

// loggerWriter：将标准库日志转发到 zap（用于 gorm logger.New）
type loggerWriter struct {
	zapLogger *zap.Logger
}

func (w loggerWriter) Write(p []byte) (n int, err error) {
	if w.zapLogger != nil {
		// 以 Info 级别写入 zap
		w.zapLogger.Info(string(p))
	} else {
		// 无 zap 时直接打印到控制台
		fmt.Print(string(p))
	}
	// 返回写入的字节数
	return len(p), nil
}
